#include <stdio.h>
#include <signal.h>

#include "ak_common.h"
#include "ak_global.h"
#include "ak_ai.h"

#define TEST_AI_SOURCE		0			/* test audio input source */
#define TEST_AI_VOLUME		0			/* test audio volume */
#define WRITE_AI_FILE		1			/* write audio data to file */
#define SYNC_PCM_FRAME_TS	0			/* sync audio frame ts */
#define SYNC_AUDIO_TIME		(10*1000)	/* sync per 10 seconds */
#define AI_DEMO_MAX_VOLUME	12			/* max volume according to ai */

struct ai_demo {
	int run_flag;		/* capture run flag */
	int save_time;		/* pcm file save time */
	char *save_path;	/* pcm file save path */
	
#if WRITE_AI_FILE	
	FILE *fp;			/* pcm file file handle */
#endif	

#if TEST_AI_SOURCE
	int source;			/* source is mic or linein */
#endif

#if TEST_AI_VOLUME
	struct ak_timeval volume_time;	/* current time */
	int cur_volume;					/* current vulume */
#endif

#if SYNC_PCM_FRAME_TS
	unsigned long long first_ts;	/* the first ts */
	unsigned long long interval_ts;	/* at the first ts per 10 seconds */
	struct ak_timeval first_time;	/* get first frame time */
	struct ak_timeval sync_time;	/* sync ts time */
#endif
};

static struct ai_demo pcm_in = {0}; /* ai information */

#if WRITE_AI_FILE
static int create_pcm_file(void)
{
	char time_str[20] = {0};
	char file_path[255] = {0};
	struct ak_date date;

	/* get the file path */
	ak_get_localdate(&date);
	ak_date_to_string(&date, time_str);
    sprintf(file_path, "%s%s.pcm", pcm_in.save_path, time_str);

	/* open file */
	pcm_in.fp = fopen(file_path, "w+");
	if(NULL == pcm_in.fp) {
		printf("open pcm file err\n");
		return AK_FAILED;
	}

	printf("open save file: %s OK\n", file_path);
	return AK_SUCCESS;
}
#endif

#if TEST_AI_SOURCE
static void switch_ai_source(void *ai_handle, unsigned long start_ts,
							unsigned long end_ts)
{
	switch (pcm_in.source) {
	case 0:
		if ((end_ts - start_ts) >= 5 * 1000) {
			pcm_in.source = 1;
			if (ak_ai_set_source(ai_handle, AI_SOURCE_LINEIN) != 0)
				ak_print_error_ex("set ai source linein fail\n");
			else
				ak_print_normal_ex("set ai source linein success\n");
		}
		break;
	case 1:
		if ((end_ts - start_ts) >= 10 * 1000) {
			pcm_in.source = 2;
			if (ak_ai_set_source(ai_handle, AI_SOURCE_AUTO) != 0)
				ak_print_error_ex("set ai source auto fail\n");
			else
				ak_print_normal_ex("set ai source auto success\n");
		}
		break;
	default:
		break;
	}
}
#endif

#if TEST_AI_VOLUME
static void test_volume_adjust(void *ai_handle)
{
	struct ak_timeval cur_time;
	
	ak_get_ostime(&cur_time);
	long diff_time = ak_diff_ms_time(&cur_time, &(pcm_in.volume_time));
	
	/* change volume every 10s */
	if(diff_time >= 10*1000) {
		/* volume change from 0~12 */
		++pcm_in.cur_volume;
		if (pcm_in.cur_volume > AI_DEMO_MAX_VOLUME) {
			pcm_in.cur_volume = 0;
		}
		
		ak_print_notice_ex("cur_volume=%d\n", pcm_in.cur_volume);
		ak_get_ostime(&(pcm_in.volume_time));
		ak_ai_set_volume(ai_handle, pcm_in.cur_volume);
	}
}
#endif

#if SYNC_PCM_FRAME_TS
static void sync_audio_frame_ts(unsigned long long cur_ts)
{
	if (pcm_in.first_ts == 0) {
		pcm_in.first_ts = cur_ts;
		pcm_in.interval_ts = cur_ts;
		ak_get_ostime(&(pcm_in.first_time));
		ak_get_ostime(&(pcm_in.sync_time));
	}
			
	struct ak_timeval cur_time;

	ak_get_ostime(&cur_time);
	long diff_time = ak_diff_ms_time(&cur_time, &(pcm_in.sync_time));

	/* calc frame number per ten seconds */
	if(diff_time >= SYNC_AUDIO_TIME) {
		unsigned long long target_ts = pcm_in.interval_ts + SYNC_AUDIO_TIME;
		unsigned long long total_ts = pcm_in.first_ts + ak_diff_ms_time(&cur_time, 
			&(pcm_in.first_time));
		ak_print_normal("*** first_ts=%llu, cur_ts=%llu, target_ts=%llu, total_ts=%llu ***\n", 
			pcm_in.first_ts, cur_ts, target_ts, total_ts);
		ak_print_normal("*** 10 sec diff=%lld, total diff=%lld ***\n\n", 
			(cur_ts - target_ts), (cur_ts - total_ts));

		ak_get_ostime(&(pcm_in.sync_time));
		pcm_in.interval_ts = cur_ts;
	}
}
#endif

static void read_ad_pcm(void *ai_handle, int volume)
{
	unsigned long long start_ts = 0;/* use to save capture start time */
	unsigned long long end_ts = 0;	/* the last frame time */
	unsigned long pre_seq_no = 0;	/* frame sequence no */
	struct frame frame = {0};

	/* set source */
	if (ak_ai_set_source(ai_handle, AI_SOURCE_MIC))
		ak_print_error_ex("set ai source mic fail\n");
	else
		ak_print_normal_ex("set ai source mic success\n");

	/* set aec,aec only support 8K sample */
	ak_ai_set_aec(ai_handle, AUDIO_FUNC_DISABLE);

	/* set nr and agc,nr&agc only support 8K sample */
	ak_ai_set_nr_agc(ai_handle, AUDIO_FUNC_ENABLE);
	// ak_ai_set_nr(ai_handle, AUDIO_FUNC_DISABLE);

	/* set resample */
	ak_ai_set_resample(ai_handle, AUDIO_FUNC_ENABLE);

	/* volume is from 0 to 12,volume 0 is mute */
	ak_ai_set_volume(ai_handle, volume);

	/* clear ai buffer */
	ak_ai_clear_frame_buffer(ai_handle);

	/* start capture will begin to get frame */
	ak_ai_start_capture(ai_handle);

#if TEST_AI_VOLUME
	/* get current time */
	ak_get_ostime(&(pcm_in.volume_time));
#endif	

	while(pcm_in.run_flag) {
		/* get the pcm data frame */
		if (ak_ai_get_frame(ai_handle, &frame, 0) < 0) {
			ak_print_error_ex("read audio from AD error!\n");
			break;
		}

#if WRITE_AI_FILE
		/* write frame to file */
		if(fwrite(frame.data, frame.len, 1, pcm_in.fp) < 0) {
    		printf("write file err\n");
    		break;
    	}
#endif    	

#if SYNC_PCM_FRAME_TS
		sync_audio_frame_ts(frame.ts);
#endif	
		/* check the sequence no */
		if (frame.seq_no != (pre_seq_no + 1)) {
			ak_print_normal_ex("audio: ts=%llu, len=%u, seq_no=%lu\n", 
    			frame.ts, frame.len, frame.seq_no);
		}
		pre_seq_no = frame.seq_no;

		/* save the begin time */
		if(0 == start_ts) {
			start_ts = frame.ts;
			end_ts = frame.ts;
		}
		end_ts = frame.ts;

		/* data write over,release */
		ak_ai_release_frame(ai_handle, &frame);

#if TEST_AI_VOLUME
		/* change volume */
		test_volume_adjust(ai_handle);
#endif
		/* time is up */
		if((end_ts - start_ts) >= pcm_in.save_time) {
			break;
		}

#if TEST_AI_SOURCE
		/* change source */
	 	switch_ai_source(ai_handle, start_ts, end_ts);
#endif
	}

	ak_print_normal("\n\t start_ts=%llu, end_ts=%llu, use=%llu(ms)\n",
		start_ts, end_ts, (end_ts - start_ts));
	ak_print_normal("\t read_ad_pcm exit\n\n");
}

static void process_signal(int sig)
{
	ak_print_notice("\t signal %d caught", sig);
	if((SIGTERM == sig) || (SIGINT == sig) || (SIGSEGV == sig)){
		pcm_in.run_flag = AK_FALSE;
	}
}

static int register_signal(void)
{
	signal(SIGSEGV, process_signal);
	signal(SIGINT, process_signal);
	signal(SIGTERM, process_signal);
	signal(SIGCHLD, SIG_IGN);

	return 0;
}

/**
 * argv[0]: ak_ai_demo
 * argv[1]: sample rate
 * argv[2]: pcm file save path
 * argv[3]: read AD time, unit: second
 * argv[4]: volume
 * note: We will generate a pcm file in the appointed file path
 * 		such as 20161123-153020.pcm
 */
int main(int argc, char **argv)
{
	if (argc != 5) {
		printf("usage: %s [sample rate] [save path] [time(secs)] [volume]\n", 
				argv[0]);
		printf("eg.: %s 8000 /mnt/ 120 7 \n", argv[0]);
		printf("note: max sample rate 48000Hz\n");
		printf("note: max volume %d, volume in[0-%d]\n",
				AI_DEMO_MAX_VOLUME, AI_DEMO_MAX_VOLUME);
			
		return AK_FAILED;
	}

	/* get volume,volume is from 0~12 */
	int volume = atoi(argv[4]);
	if (volume < 0 || volume > AI_DEMO_MAX_VOLUME) {	
		printf("volume is 0~%d\n", AI_DEMO_MAX_VOLUME);
		return AK_FAILED;
	}

	printf("----- %s -----\n", argv[0]);
	printf("version: %s\n\n", ak_ai_get_version());

	/* get save time(s) */
	pcm_in.save_time = atoi(argv[3]) * 1000;
	/* get save path */
	pcm_in.save_path = argv[2];

#if WRITE_AI_FILE
	if(NULL == pcm_in.fp) {
		if(AK_FAILED == create_pcm_file()) {
			return AK_FAILED;
		}
	}
#endif	

	/* register signal */
	register_signal();

	struct pcm_param param = {0};

	/* sample bits only support 16 bit */
	param.sample_bits = 16;
	/* channel number only support AUDIO_CHANNEL_MONO */
	param.channel_num = AUDIO_CHANNEL_MONO;
	/* get sample rate */
	param.sample_rate = atoi(argv[1]);

	/* ai open */
    void *ai_handle = ak_ai_open(&param);
    if(NULL == ai_handle) {
    	return AK_FAILED;
    }

	/* ak_ai_set_frame_interval must use before ak_ai_get_frame */
	if(AK_SUCCESS == ak_ai_set_frame_interval(ai_handle, AUDIO_DEFAULT_INTERVAL)) {
		ak_print_normal_ex("frame interval=%d\n", AUDIO_DEFAULT_INTERVAL);	
	}

	pcm_in.run_flag = AK_TRUE;
	/* capture begin */
    read_ad_pcm(ai_handle, volume);

#if WRITE_AI_FILE
	/* close file handle */
    if(NULL != pcm_in.fp) {
    	fclose(pcm_in.fp);
    	pcm_in.fp = NULL;
    }
#endif

	/* close ai */
    ak_ai_close(ai_handle);
    printf("----- %s exit -----\n", argv[0]);

	return 0;
}
